home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-12-07 | 22.2 KB | 915 lines | [TEXT/CWIE] |
- /*
- ********************************************************************************
- **
- ** File: main.cp
- **
- ** Description:
- **
- ** This is the code for Rumor Mill, a simple application that allows users
- ** in the same zone to send "rumors" to each other. The app registers itself
- ** via NBP, and then sends DDP packets to other Rumor Mill applications
- ** on the net when the user enters a new rumor and presses the "send" button
- ** or presses the <Return> key.
- **
- ** A DDP socket listener is installed by the application. The socket listener
- ** was taken directly from Inside Macintosh: Networking.
- **
- ** Author: Cary A. Farrier, cary@laserpoint.com, <http://www.laserpoint.com>
- **
- ** Functions:
- **
- ** main(); - program entry
- ** InitAppleTalk(); - network driver initialization
- **
- ** CRumorMillApp::
- ** CRumorMillApp(); - c'tor
- ** ~CRumorMillApp(); - d'tor
- ** StartUp(); - pre-execution init
- ** ObeyCommand(); - command execution
- ** FindCommandStatus(); - menu state checking
- ** ListenToMessage(); - message execution
- ** NetStart(); - application network startup
- ** FindOtherMills(); - search for other Rumor Mills
- ** RegisterName(); - register app on network
- ** UnregisterName(); - unregister app on network
- ** NetStop(); - shutdown app network connection
- ** SendRumor(); - send a rumor to other rumor mills
- ** DisplayRumor(); - display queued rumors received
- ** ProcessNextEvent(); - pull incoming DDP packets from queues
- ** SetStatusMessage(); - set status area message
- ** GetStatusMessage(); - get status area message
- ** KeyFilterFunction(); - check for <Return> key
- **
- ********************************************************************************
- **
- ** Revision History:
- **
- ** 12/06/97 Farrier Created.
- **
- ********************************************************************************
- */
- #include "main.h"
-
- #include <LGrowZone.h>
- #include <LWindow.h>
- #include <PP_Messages.h>
- #include <PP_Resources.h>
- #include <PPobClasses.h>
- #include <UDrawingState.h>
- #include <UMemoryMgr.h>
- #include <URegistrar.h>
- #include <LEditField.h>
- #include <LGroupBox.h>
- #include <UKeyFilters.h>
-
- #include <AppleTalk.h>
-
- #include <string.h>
-
- /*
- ********************************************************************************
- ** externals from listener.asm
- ********************************************************************************
- */
- extern pascal OSErr SL_InitSktListener( QHdr *inFreeQueue, QHdr *inUsedQueue );
- extern pascal void SL_TheListener( void );
-
- /*
- ********************************************************************************
- ** constants
- ********************************************************************************
- */
- #define kDDPMaxData 586
- #define kNumPacketBuffers 10
- #define kMaxMessages 25
-
- /*
- ********************************************************************************
- ** structures
- ********************************************************************************
- */
- struct Message {
- QElemPtr qLink;
- Boolean used;
- Str255 message;
- };
- typedef struct Message Message, *MessagePtr, **MessageHandle;
-
- struct PacketBuffer {
- QElemPtr qLink;
- short qType;
- short buffer_Type;
- short buffer_NodeID;
- AddrBlock buffer_Address;
- short buffer_Hops;
- short buffer_ActCount;
- OSErr buffer_CheckSum;
- char buffer_Data[kDDPMaxData];
- };
- typedef struct PacketBuffer PacketBuffer, *PacketBufferPtr, **PacketBufferHandle;
-
- /*
- ********************************************************************************
- ** globals
- ********************************************************************************
- */
- short gMPPDriverRef;
- NamesTableEntry gMyNTE;
- Str32 gMyName;
- UInt8 gTheSocket;
- PacketBuffer gPacketBuffers[kNumPacketBuffers];
- QHdr gFreePacketQueue, gUsedPacketQueue, gMessageQueue;
- Uint8 gOtherRumorMills[256];
- Message gMessageStorage[kMaxMessages];
- CRumorMillApp *gApp;
-
- /*
- ********************************************************************************
- ** resource ids
- ********************************************************************************
- */
- #define window_Sample 128
- #define alert_Error 129
-
- /*
- ********************************************************************************
- ** pane ids
- ********************************************************************************
- */
- #define button_Send 1
- #define ledit_IncomingRumor 10
- #define ledit_NewRumor 11
- #define lcapt_Status 12
-
- /*
- ********************************************************************************
- ** message ids
- ********************************************************************************
- */
- #define msg_SendRumor 1000
-
- /*
- ********************************************************************************
- ** other data
- ********************************************************************************
- */
- const Str32 str32_RumorMill = "\pRumorMill";
-
- /*
- ********************************************************************************
- ** prototypes
- ********************************************************************************
- */
- Boolean InitAppleTalk( void );
-
- /*
- ********************************************************************************
- **
- ** Function: main()
- **
- ** Description:
- **
- ** Program entry point. Sets up the heap and runs the application object.
- **
- ********************************************************************************
- */
- void main(void)
- {
- // Set Debugging options
- SetDebugThrow_(debugAction_Alert);
- SetDebugSignal_(debugAction_Alert);
-
- InitializeHeap(3); // Initialize Memory Manager
- // Parameter is number of Master Pointer
- // blocks to allocate
-
- // Initialize standard Toolbox managers
- UQDGlobals::InitializeToolbox(&qd);
-
- new LGrowZone(20000); // Install a GrowZone function to catch
- // low memory situations.
-
- // initialize appletalk
- if( FALSE == InitAppleTalk() )
- return;
-
- CRumorMillApp theApp; // replace this with your App type
- gApp = &theApp;
- theApp.Run();
- }
-
- /*
- ********************************************************************************
- **
- ** Function: InitAppleTalk()
- **
- ** Description:
- **
- ** Checks for the correct version of AppleTalk (phase 2), and then opens
- ** the low level AppleTalk driver, ".MPP", that contains DDP.
- **
- ********************************************************************************
- */
- Boolean
- InitAppleTalk( void )
- {
- OSErr theErr;
- SysEnvRec theEnvirons;
-
- // check for appletalk phase 2
- theErr = SysEnvirons(curSysEnvVers, &theEnvirons);
- if(theEnvirons.atDrvrVersNum <= 52)
- {
- ::ParamText("\pThis program requires a later version of AppleTalk", NULL, NULL, NULL);
- ::StopAlert( alert_Error, NULL );
- return FALSE;
- }
-
- // open the .MPP driver
- theErr = OpenDriver( "\p.MPP", &gMPPDriverRef );
- if ( noErr != theErr )
- {
- ::ParamText("\pError opening .MPP driver", NULL, NULL, NULL);
- ::StopAlert( alert_Error, NULL );
- return FALSE;
- }
-
- return TRUE;
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::CRumorMillApp()
- **
- ** Description:
- **
- ** Constructor. Registers PowerPlant classes.
- **
- ********************************************************************************
- */
- CRumorMillApp::CRumorMillApp()
- {
- // Register functions to create core PowerPlant classes
-
- RegisterAllPPClasses();
- URegistrar::RegisterClass(LGroupBox::class_ID, LGroupBox::CreateGroupBoxStream);
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::~CRumorMillApp()
- **
- ** Description:
- **
- ** Destructor. Closes down network connection.
- **
- ********************************************************************************
- */
- CRumorMillApp::~CRumorMillApp()
- {
- NetStop();
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::Startup()
- **
- ** Description:
- **
- ** Called after application object has been created and initialized, but
- ** before it begins the main event loop. A new window is created here and
- ** the network connection is started.
- **
- ********************************************************************************
- */
- void
- CRumorMillApp::StartUp()
- {
- ObeyCommand(cmd_New, nil);
-
- NetStart();
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::ObeyCommand()
- **
- ** Description:
- **
- ** Command handler for the application class. Currently only responds to
- ** the cmd_New command to create a new window. For each window created,
- ** the application is setup as a listener for the "send" button and a key
- ** filter is installed for the edit control to detect the <Return> key
- ** being pressed (so that the rumor can be sent out).
- **
- ********************************************************************************
- */
- Boolean
- CRumorMillApp::ObeyCommand(
- CommandT inCommand,
- void *ioParam)
- {
- Boolean cmdHandled = true;
- LStdButton *theButton;
- LWindow *theWindow;
- LEditField *theEdit;
-
- switch (inCommand) {
-
- case cmd_New:
- theWindow = LWindow::CreateWindow(window_Sample, this);
- if( theWindow )
- {
- theWindow->Show();
- theButton = (LStdButton *)theWindow->FindPaneByID( button_Send );
- if( theButton )
- theButton->AddListener( this );
-
- theEdit = (LEditField *)theWindow->FindPaneByID( ledit_NewRumor );
- if( theEdit )
- {
- theEdit->SetKeyFilter( CRumorMillApp::KeyFilterFunction );
- LCommander::SwitchTarget( theEdit );
- }
- }
- break;
-
- default:
- cmdHandled = LApplication::ObeyCommand(inCommand, ioParam);
- break;
- }
-
- return cmdHandled;
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::FindCommandStatus()
- **
- ** Description:
- **
- ** Returns the enabled/disabled/checked state of the menus.
- **
- ********************************************************************************
- */
- void
- CRumorMillApp::FindCommandStatus(
- CommandT inCommand,
- Boolean &outEnabled,
- Boolean &outUsesMark,
- Char16 &outMark,
- Str255 outName)
- {
-
- switch (inCommand) {
-
- // Return menu item status according to command messages.
- // Any that you don't handle will be passed to LApplication
-
- case cmd_New:
- outEnabled = true;
- break;
-
- default:
- LApplication::FindCommandStatus(inCommand, outEnabled,
- outUsesMark, outMark, outName);
- break;
- }
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::ListenToMessage()
- **
- ** Description:
- **
- ** Message handler. Responds to the "send rumor" message by sending the
- ** rumor out over the network.
- **
- ********************************************************************************
- */
- void
- CRumorMillApp::ListenToMessage(
- MessageT inMessage,
- void *ioParam
- )
- {
- LWindow *theWindow = NULL;
- LEditField *theEdit;
- Str255 theText;
-
- switch( inMessage )
- {
- case msg_SendRumor:
- theWindow = LWindow::FetchWindowObject( FrontWindow() );
- if( theWindow )
- {
- theEdit = (LEditField *)theWindow->FindPaneByID( ledit_NewRumor );
- if( theEdit )
- {
- theEdit->GetDescriptor( theText );
- if( theText[0] )
- SendRumor( theText );
- }
- }
- break;
-
- default:
- break;
- }
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::NetStart()
- **
- ** Description:
- **
- ** Application specific network startup. Creates a socket listener with
- ** buffer queues, opens a socket, and registers the application via NBP.
- **
- ********************************************************************************
- */
- Boolean
- CRumorMillApp::NetStart( void )
- {
- OSErr theErr;
- MPPParamBlock thePB;
- Byte theSocket;
- int i;
-
- // initialize socket listener
- for( i = 0; i < kNumPacketBuffers; i++ )
- Enqueue( (QElem *)&gPacketBuffers[i], &gFreePacketQueue );
- theErr = SL_InitSktListener( &gFreePacketQueue, &gUsedPacketQueue );
- if( theErr )
- {
- ::ParamText("\pError initializing socket listener", NULL, NULL, NULL);
- ::StopAlert( alert_Error, NULL );
- return false;
- }
-
- // open socket
- memset( &thePB, 0, sizeof( thePB ) );
- thePB.DDPlistener = SL_TheListener;
- theErr = POpenSkt( &thePB, FALSE );
- if( noErr != theErr )
- {
- ::ParamText("\pError opening socket", NULL, NULL, NULL);
- ::StopAlert( alert_Error, NULL );
- return false;
- }
- gTheSocket = thePB.DDPsocket;
-
- // register via nbp
- if( FALSE == RegisterName( gTheSocket ) )
- return FALSE;
-
- return TRUE;
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::FindOtherMills()
- **
- ** Description:
- **
- ** Searches for other Rumor Mill applications via NBP. This takes a few
- ** seconds so the watch cursor is displayed.
- **
- ********************************************************************************
- */
- Boolean
- CRumorMillApp::FindOtherMills( void )
- {
- OSErr theErr;
- MPPParamBlock thePB;
- int i;
- char theLookupBuffer[256*104];
- NamesTableEntry theEntity;
- EntityName theName;
- AddrBlock theAddress;
- Str255 theOldStatus;
-
- GetStatusMessage( theOldStatus );
- SetStatusMessage( "\pLooking for someone to gossip with…" );
-
- SetCursor( *(GetCursor( watchCursor )) );
-
- // find other rumor mills in this zone & add to list
- memset( &theEntity, 0, sizeof( theEntity ) );
- NBPSetEntity( (Ptr)&theEntity.nt.entityData,
- (ConstStr32Param)"\p=", (ConstStr32Param)str32_RumorMill, "\p*" );
-
- memset( &thePB, 0, sizeof( thePB ) );
- thePB.NBP.ioRefNum = gMPPDriverRef;
- thePB.NBPinterval = 7;
- thePB.NBPcount = 5;
- thePB.NBPentityPtr = (Ptr)&theEntity.nt.entityData;
- thePB.NBPretBuffPtr = theLookupBuffer;
- thePB.NBPretBuffSize = sizeof(theLookupBuffer);
- thePB.NBPmaxToGet = 256;
- theErr = PLookupName( &thePB, false );
- if( theErr )
- {
- ::ParamText("\pError from PLookupName", NULL, NULL, NULL);
- ::StopAlert( alert_Error, NULL );
- SetStatusMessage( theOldStatus );
- SetCursor( &qd.arrow );
- return false;
- }
- if( thePB.NBPnumGotten > 0 )
- {
- memset( gOtherRumorMills, 0, sizeof( gOtherRumorMills ) );
- for( i = 1; i <= thePB.NBPnumGotten; i++ )
- {
- theErr = NBPExtract( theLookupBuffer, thePB.NBPnumGotten, i,
- &theName, &theAddress );
- if( noErr == theErr )
- {
- gOtherRumorMills[theAddress.aNode] = theAddress.aSocket;
- }
- }
- }
-
- SetStatusMessage( theOldStatus );
- SetCursor( &qd.arrow );
-
- return TRUE;
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::RegisterName()
- **
- ** Description:
- **
- ** Registers the application on the network using NBP. The user's name
- ** (entered in the Sharing Setup control panel) is used as the name.
- **
- ********************************************************************************
- */
- Boolean
- CRumorMillApp::RegisterName(
- UInt8 inSocket
- )
- {
- MPPParamBlock pb;
- StringHandle userName;
-
- userName = GetString(-16096);
-
- gMyName[0] = 0;
- if (userName != nil)
- {
- if (*userName[0] != 0)
- BlockMove(*userName,&gMyName,*userName[0]+1);
- }
-
- NBPSetNTE( (Ptr)&gMyNTE, (ConstStr32Param)&gMyName, (ConstStr32Param)str32_RumorMill,
- "\p*", inSocket );
-
- pb.NBPinterval = 2;
- pb.NBPcount = 3;
- pb.NBPentityPtr = (Ptr)&gMyNTE;
- pb.NBPverifyFlag = 1;
- if ( PRegisterName( &pb,false ) != noErr )
- {
- ::ParamText("\pError registering name", NULL, NULL, NULL);
- ::StopAlert( alert_Error, NULL );
- return false;
- }
-
- return true;
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::UnregisterName()
- **
- ** Description:
- **
- ** Removes our registered name from the network.
- **
- ********************************************************************************
- */
- Boolean
- CRumorMillApp::UnregisterName( void )
- {
- MPPParamBlock pb;
- OSErr err;
-
- pb.NBPentityPtr = (Ptr)&gMyNTE.nt.entityData;
- err = PRemoveName(&pb, false);
-
- return TRUE;
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::NetStop()
- **
- ** Description:
- **
- ** Closes down our socket.
- **
- ********************************************************************************
- */
- Boolean
- CRumorMillApp::NetStop( void )
- {
- OSErr theErr;
- MPPParamBlock thePB;
- Byte theSocket;
-
- // unregister via nbp
- UnregisterName();
-
- // close socket
- memset( &thePB, 0, sizeof( thePB ) );
- thePB.DDPsocket = gTheSocket;
- theErr = PCloseSkt(&thePB, FALSE);
- if( noErr != theErr )
- {
- ::ParamText("\pError closing socket", NULL, NULL, NULL);
- ::StopAlert( alert_Error, NULL );
- }
-
- return TRUE;
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::SendRumor()
- **
- ** Description:
- **
- ** Scans the net for other Rumor Mills, then sends a rumor to the other
- ** mills found.
- **
- ********************************************************************************
- */
- Boolean
- CRumorMillApp::SendRumor(
- Str255 inRumorText
- )
- {
- int i;
- MPPParamBlock thePB;
- char theWDS[14], theHeader[17];
- AddrBlock theAddress;
- OSErr theErr;
-
- // find all the mills out there
- FindOtherMills();
-
- // find first known mill
- for( i = 0; i < 256; i++ )
- if( gOtherRumorMills[i] )
- break;
-
- // send to all known rumor mills
- if( i < 256 )
- {
- for( /* empty */ ; i < 256; i++ )
- {
- if( gOtherRumorMills[i] )
- {
- // setup address
- theAddress.aNet = 0;
- theAddress.aNode = i;
- theAddress.aSocket = gOtherRumorMills[i];
-
- // build wds
- BuildDDPwds( theWDS, theHeader, (char *)inRumorText, theAddress, 16, inRumorText[0]+1 );
-
- // build paramblock
- memset( &thePB, 0, sizeof( thePB ) );
- thePB.DDPsocket = gTheSocket;
- thePB.DDPwdsPointer = (char *)&theWDS;
-
- // write data
- theErr = PWriteDDP(&thePB, FALSE);
- if( noErr != theErr )
- {
- ::ParamText("\pError writing to socket", NULL, NULL, NULL);
- ::StopAlert( alert_Error, NULL );
- }
- }
- }
- }
-
- return TRUE;
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::DisplayRumor()
- **
- ** Description:
- **
- ** This function is called from our main event processing loop, and every
- ** five seconds it will check to see if a rumor is available in the incoming
- ** rumor messages queue. If so, it will display the new message.
- **
- ********************************************************************************
- */
- Boolean
- CRumorMillApp::DisplayRumor( void )
- {
- static long theLastTime = 0;
- long theCount;
- MessagePtr theMessage;
- LWindow *theWindow;
- LEditField *theEdit;
-
- // display new messages every 5 seconds
- theCount = TickCount();
- if( theCount - theLastTime > 5*60 )
- {
- theLastTime = theCount;
- if( gMessageQueue.qHead )
- {
- // extract message
- theMessage = (MessagePtr)gMessageQueue.qHead;
- Dequeue( (QElemPtr)theMessage, &gMessageQueue );
-
- // display message
- theWindow = LWindow::FetchWindowObject( FrontWindow() );
- if( theWindow )
- {
- theEdit = (LEditField *)theWindow->FindPaneByID( ledit_IncomingRumor );
- if( theEdit )
- theEdit->SetDescriptor( theMessage->message );
- }
-
- // make message available for reuse
- theMessage->used = FALSE;
- }
- }
-
- return TRUE;
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::ProcessNextEvent()
- **
- ** Description:
- **
- ** Overrides the LApplication::ProcessNextEvent() so that we can pull incoming
- ** DDP packets out of the queue and move them into the rumor message queue
- ** for display. Also calls DisplayRumor() before passing control to the
- ** real event handler in the base class.
- **
- ********************************************************************************
- */
- void
- CRumorMillApp::ProcessNextEvent()
- {
- PacketBuffer *thePacket;
- MessagePtr theMessage;
- int i;
-
- // retrieve pending ddp packets
- while( gUsedPacketQueue.qHead )
- {
- // retrieve the packet
- thePacket = (PacketBufferPtr)gUsedPacketQueue.qHead;
-
- // find space for the new message, if available
- // if no space we'll just ignore the packet
- theMessage = gMessageStorage;
- for( i = 0; i < kMaxMessages; i++ )
- if( FALSE == theMessage->used )
- break;
- if( i < kMaxMessages )
- {
- // mark message used
- theMessage->used = TRUE;
-
- // copy the data
- ::BlockMove( thePacket->buffer_Data, theMessage->message,
- thePacket->buffer_Data[0]+1 );
-
- // enqueue it
- Enqueue( (QElemPtr)theMessage, &gMessageQueue );
- }
- // remove packet from used queue
- Dequeue( (QElemPtr)thePacket, &gUsedPacketQueue );
-
- // add packet to free queue
- Enqueue( (QElemPtr)thePacket, &gFreePacketQueue );
- }
-
- // display any pending rumors
- DisplayRumor();
-
- // let the base class process events normally now
- LApplication::ProcessNextEvent();
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::SetStatusMessage()
- **
- ** Description:
- **
- ** Sets the status message area text.
- **
- ********************************************************************************
- */
- void
- CRumorMillApp::SetStatusMessage(
- Str255 inStatus
- )
- {
- LWindow *theWindow;
- LCaption *theCaption;
-
- theWindow = LWindow::FetchWindowObject( FrontWindow() );
- if( theWindow )
- {
- theCaption = (LCaption *)theWindow->FindPaneByID( lcapt_Status );
- if( theCaption )
- {
- theCaption->SetDescriptor( inStatus );
- theCaption->Draw( NULL );
- }
- }
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::GetStatusMessage()
- **
- ** Description:
- **
- ** Returns the status area text.
- **
- ********************************************************************************
- */
- void
- CRumorMillApp::GetStatusMessage(
- Str255 outStatus
- )
- {
- LWindow *theWindow;
- LCaption *theCaption;
-
- theWindow = LWindow::FetchWindowObject( FrontWindow() );
- if( theWindow )
- {
- theCaption = (LCaption *)theWindow->FindPaneByID( lcapt_Status );
- if( theCaption )
- theCaption->GetDescriptor( outStatus );
- }
- }
-
- /*
- ********************************************************************************
- **
- ** Function: CRumorMillApp::KeyFilterFunction()
- **
- ** Description:
- **
- ** This key filter is installed in the window's edit field so that when a
- ** <Return> is detected the text the user has entered will be sent out
- ** to the other rumor mills.
- **
- ********************************************************************************
- */
- EKeyStatus
- CRumorMillApp::KeyFilterFunction(
- const EventRecord& inKeyEvent
- )
- {
- LWindow *theWindow = NULL;
- LEditField *theEdit;
- Str255 theText;
-
- // if the user presses the return key then send the message
- if( (inKeyEvent.message & 0xFF) == '\r' )
- {
- gApp->ListenToMessage( msg_SendRumor, NULL );
- return keyStatus_Ignore;
- }
-
- return keyStatus_Input;
- }